#ifndef __TCPCONNECTION_H__
#define __TCPCONNECTION_H__

#include "Spark/Utils/NonCopyable.h"
#include "Spark/Net/MsgBuffer.h"
#include "Spark/Net/CallbackDefs.h"
#include "Spark/Net/SocketAddr.h"
#include "Spark/Utils/Any.h"
#include <mutex>
#include <memory>
#include <atomic>
#include <map>

class Channel;
class EventLoop;
class Socket;
struct tcp_info;

class TcpConnection : public NonCopyable,
                      public std::enable_shared_from_this<TcpConnection>
{
  public:
    TcpConnection(EventLoop *loop,
                  int fd,
                  std::string name,
                  const SocketAddr &local,
                  const SocketAddr &peer);
    ~TcpConnection();

  public:
    EventLoop *getLoop() const
    {
        return loop_;
    }
    const SocketAddr &localAddr() const
    {
        return local_;
    }
    const SocketAddr &peerAddr() const
    {
        return peer_;
    }
    const std::string &name() const
    {
        return name_;
    }
    bool isReading() const
    {
        return reading_;
    }

    bool getTcpInfo(struct tcp_info *tcpi) const;

    std::string getTcpInfoString() const;

    void send(MsgBuffer *buf);
    void send(std::string message);

    void setConnectionCallback(ConnectionCallback_t cb)
    {
        connectionCallback_ = std::move(cb);
    }

    void setMessageCallback(ConnectionCallbackBuffer_t cb)
    {
        messageCallback_ = std::move(cb);
    }

    void setWriteDoneCallback(ConnectionCallback_t cb)
    {
        writeDoneCallback_ = std::move(cb);
    }

    void setCloseCallback(ConnectionCallback_t cb)
    {
        connectionCloseCallback_ = std::move(cb);
    }

    void setHighWatermarkCallback(ConnectionCallbackSize_t cb)
    {
        highWatermarkCallback_ = std::move(cb);
    }

    void setContext(const linb::any &context)
    {
        context_ = context;
    }
    void setContext(linb::any &&context)
    {
        context_ = std::move(context);
    }

    linb::any* getMutableContext()
    {
        return &context_;
    }

    void setTcpNoDelay(socklen_t optval);
    void connectEstablished();
    void connectDestroyed();
    void forceClose();
    void shutdown();
    void startRead();
    void stopRead();

    MsgBuffer *inputBuffer()
    {
        return &inputBuffer_;
    }

    MsgBuffer *outputBuffer()
    {
        return &outputBuffer_;
    }

    enum class TcpState
    {
        Disconnected,
        Connecting,
        Connected,
        Disconnecting
    };

    bool connected() const
    {
        return state_ == TcpState::Connected;
    }
    bool disconnected() const
    {
        return state_ == TcpState::Disconnected;
    }

  protected:
    void handleRead(TimePoint receiveTime);
    void handleWrite();
    void handleClose();
    void handleError();
    void sendInLoop(const void *msg, size_t len);
    void shutdownInLoop();
    void startReadInLoop();
    void stopReadInLoop();

  protected:
    EventLoop *loop_;
    std::unique_ptr<Socket> socket_;
    std::unique_ptr<Channel> channel_;
    const SocketAddr local_;
    const SocketAddr peer_;
    const std::string name_;
    std::atomic<TcpState> state_;
    size_t highWatermark_;
    std::mutex sendMutex_;
    bool reading_{true};
    ConnectionCallback_t connectionCallback_;
    ConnectionCallbackBuffer_t messageCallback_;
    ConnectionCallback_t writeDoneCallback_;
    ConnectionCallbackSize_t highWatermarkCallback_;
    ConnectionCallback_t connectionCloseCallback_;
    MsgBuffer inputBuffer_;
    MsgBuffer outputBuffer_;
    linb::any context_;
};

#endif  // __TCPCONNECTION_H__